﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SwinGame;
using System.Drawing;


namespace SimpleSurvival.src
{
    //-----------------------------------------------//
    //Interface - IEntity.cs
    //-----------------------------------------------//
    //Base class for all game objects
    //Added for better loose coupling
    //Although it isn't really neccessary
    //as Entity is the only thing that implements it
    public interface IEntity    
    {
        Map Map { get; }
        Sprite  Sprite { get; }
        float X { get; set; }
        float Y { get; set; }
        Vector CenterPoint { get; }
        Vector Position { get; }
        IEntity Target { set; get; }
        string  Name { set; get; }

        void    Update(double dt);

        //Collision Stuff
        bool CanCollide { set; get; }
        float Speed { set; get; }
        float Mass { get; }
        bool IsFixedPosition { get; }
        void Move(Vector v, double dt);

        // OnCollision runs twice for more realistic looking physics sim
        void OnCollision(IEntity other, double dt);     
        IController Controller { set; get; }
    }

    //-----------------------------------------------//
    //CLASS - Entity.cs
    //-----------------------------------------------//
    public abstract class Entity : IEntity
    {
        //Next time - Externalise all this
        public const int INTERSECT_RADIUS = 32 * 2;       
        public const int RESOLVE_SPEED = 50;    
        public const float FIXED_MASS = 999999999999999999999999999999999999f;
        public const float MAX_FORCE = 10f;

        //Next time - Externalise all this too
        String _name = "";
        float _speed = 1.0f;
        float _mass = 1.0f / 10.0f;
        bool _canCollide = false;
        bool _fixedPosition = false; 

        //Can access everything in the map facade
        Map _map = null;

        //Can have a controller
        IController _controller = null;

        //Has an image
        Sprite _sprite = null;

        //Can have a target ientity
        IEntity _target = null;

        #region accessors
        public Map Map { get { return _map; } }

        public Vector Position { get {  return new Vector { X = X, Y = Y }; } }

        public bool IsFixedPosition
        {
            get { return _fixedPosition; }
            set { _fixedPosition = value; }
        }
        public float Mass
        {
            get { if (IsFixedPosition) { return FIXED_MASS; } else { return _mass; } }
            set { _mass = value; }
        }

        public float Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }
        public bool CanCollide
        {
            get { return _canCollide; }
            set { _canCollide = value; }
        }
        public IController Controller
        {
            get { return _controller; }
            set { _controller = value; }
        }
        public Sprite Sprite
        {
            get { return _sprite; }
        }
        public float X { get { return _sprite.X; } set { _sprite.X = value; } }

        public float Y { get { return _sprite.Y; } set { _sprite.Y = value; } }

        public Vector CenterPoint { get { return _sprite.CenterPoint; } }

        public IEntity Target
        {
            get { return _target; }
            set { _target = value; }
        }
        public String Name
        {
            get { return _name; }
            set { _name = value; }
        } 
        #endregion

        public Entity(string name, Vector pos, Map m, EntityDrawer ed, EntityDefinitions defs)
        {
            _map = m;
            Random r = new Random();    //Move this to parent scope - Shouldn't be making new ones each time
            _name = name;
            _sprite = new Sprite(ed.GetEntityTypeBitmap(name,defs));    //Load Sprite from data files
            X = pos.X;
            Y = pos.Y;
            _sprite.CollisionKind = CollisionTestKind.AABBCollisions;
            _speed = Convert.ToInt16(defs.GetEntityDefValue(name,
                EntityDefinitions.FieldName.ENTITY_SPEED));

            Map.EntityCreatedWithinMap(this);
            //add to map?
        }

        public virtual void Update(double dt)
        {
            if (CanCollide) //check for collisions with every other entity in the map
            {
                for (int i = 0; i < Map.Entitys.Count; i++)
                {
                    if (Map.Entitys[i].Sprite != Sprite)
                    {
                        CheckEntityCollision(Map.Entitys[i], dt);
                    }
                }
            }
            //get input from controller
            if (_controller != null) { _controller.GetInput(dt); }  //optimise
        }

        public bool MightIntersect(IEntity u)  
        {
            //Check distance squared is within radius squared
            float d2 = CORE.GetDist2(_sprite.CenterPoint, u.CenterPoint);
            return (d2 < INTERSECT_RADIUS * INTERSECT_RADIUS);
        }

        //When entity collides with an other entity
        public virtual void OnCollision(IEntity Ent, double dt) //protected
        {
            if (Ent.CanCollide) //respond to solid entities
            {
                Vector dv = CORE.GetDirectionalVector(Ent.CenterPoint, CenterPoint);

                float forceRatio = Ent.Mass / Mass;                             //optimize - use 1/mass as a member instead
                if (forceRatio > MAX_FORCE) { forceRatio = MAX_FORCE; }         //cap force
                X = X - dv.X * (float)dt * RESOLVE_SPEED * forceRatio;
                Y = Y - dv.Y * (float)dt * RESOLVE_SPEED * forceRatio;
            }
        }

        void CheckEntityCollision(IEntity other, double dt)
        {
            if (MightIntersect(other))
                if (_sprite.CollisionWithSprite(other.Sprite))
                {
                    OnCollision(other, dt); //self collision
                    if (other.CanCollide)
                    {
                        other.OnCollision(this, dt);    //other collision
                    }
                }
        }

        //runs twice//
        public virtual void Move(Vector v, double dt)   //will call colide on self and target
                                                        //OnColide will likely be called by other entity as well
        {
                X = X + v.X * (float)(dt * Speed);
                Y = Y + v.Y * (float)(dt * Speed);
        }

        protected virtual void Dispose(bool b)
        {
            _sprite.Dispose();
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}
